﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Collections;

namespace CalcEng
{
  public  class CalcPara
    {
        public string filename;
        public StreamWriter outstream;
        public string code;
    }

  public class CalcResult
    {
        public string code;
        public string date;
        public float iav;
        public float jav;
        public float mav;
        public float weighted;
        public int vol;
        public int avgvolb;
        public int avgvols;
        public int avgvolnb;
        public int avgvolns;
        public int bidAM;
        public int bidPM;
        public string dataFile;
        public string strName;
        public float fClosePrice;

        public CalcResult(string code)
        {
            this.code = code;
            date = "";
            iav = 0.0F;
            jav = 0.0F;
            mav = 0.0F;
            weighted = 0.0F;
            vol = 0;
            avgvolb = 0;
            avgvols = 0;
            avgvolnb = 0;
            avgvolns = 0;
            bidAM = 0;
            bidPM = 0;
            dataFile = "";
        }
    }

    public class Trade
    {
        public float price;
        public int   vol;
        public int op;
        public char flag;
        public Trade()
        {
            price = 0.0F;
            vol = 0;
            op = 0;
            flag = '\0';
        }
    }

    class MValue
    {
       public  int   vol;
       public float m;
       public MValue()
       {
            vol = 0;
            m = 0.0F;
       }
    }

    public class CalcEngine
    {
        public static void FormatOutfileHead(StreamWriter sw)
        {
            string head = "code\x09 Date\x09 Name\x09 Price\x09 ABS\x09 NBS\x09 DDNBS\x09 WT\x09 Vol\x09 VolAvgB\x09 VolAvgS\x09 VolAvgNB\x09 VolAvgNS\x09 BidAM\x09 BidPM\x09 FilePath";
            sw.WriteLine(head);
        }

        public static CalcResult Calc(CalcPara calcpara)
        {
            string date = "";
            string strStockname = "";
            List<Trade> arr = new List<Trade>();
            int voltotal = 0;
            int voltotalB = 0;
            int voltotalS = 0;
            int cntB = 0;
            int cntS = 0;
            int cntNB = 0;
            int cntNS = 0;
            float fClosePrice = 0;

            float pricetotal = 0;
            if (!File.Exists(calcpara.filename))
            {
               calcpara.filename = calcpara.filename.Replace(".xls", ".txt");
            }

            CalcResult cRet = new CalcResult(calcpara.code);

            try
            {
                using (StreamReader sr = new StreamReader(calcpara.filename, System.Text.Encoding.Default))
                {
                    string title = sr.ReadLine();
                    string[] titles = title.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries);
                    int code = Convert.ToInt32(cRet.code);
                    date = titles[0];
                    
                   /* strStockname = title.Replace(titles[0], "");
                    strStockname = strStockname.Replace(titles[titles.Length - 1], "");
                    strStockname = strStockname.Trim();*/
                    strStockname = title.Remove(0, title.IndexOf(titles[1]));
                    strStockname = strStockname.Substring(0, strStockname.IndexOf('(')).Trim();
                    for (int ii = 0; ii < 4; ii++)
                    {
                        sr.ReadLine();//skip 4 lines
                    }                    

                    string line = sr.ReadLine();
                    int nLine = 1;
                    while (line != null)
                    {
                        string[] words = line.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries);
                        if (words.Count() > 4)
                        {
                            Trade tr = new Trade();
                            if( !float.TryParse(words[1], out tr.price) )
                                Console.WriteLine( string.Format("can't parse price in line{0}: {1} ", nLine, line));
                            if( !int.TryParse(words[2], out tr.vol))
                                Console.WriteLine(string.Format("can't parse vol in line{0}: {1} ", nLine, line));
                            if( !int.TryParse(words[3], out tr.op) )
                                Console.WriteLine(string.Format("can't parse op in line{0}: {1} ", nLine, line));
                            tr.flag = words[4].ElementAt<char>(0);
                            voltotal += tr.vol;
                            pricetotal += tr.price;
                            //fClosePrice = tr.price;
                            arr.Add(tr);
                            if (tr.flag == 'B')
                            {
                                voltotalB += tr.vol;
                                cntB++;
                                cntNB += tr.op;
                            }
                            else if (tr.flag == 'S')
                            {
                                voltotalS += tr.vol;
                                cntS++;
                                cntNS += tr.op;
                            }
                        }
                        else if (words.Count() == 4)
                        {
                            if (words[0].StartsWith("09:2"))
                            {
                                int bid;
                                int.TryParse(words[2], out bid);
                                cRet.bidAM += bid;
                                voltotal += bid;
                            }
                            else if (words[0] == "15:00" || words[0].StartsWith("14:5"))
                            {
                                int.TryParse(words[2], out cRet.bidPM);
                                voltotal += cRet.bidPM;
                                float.TryParse(words[1], out fClosePrice);
                            }
                            //Console.WriteLine(string.Format("can't parse line{0}: {1}", nLine, line) );
                        }
                        line = sr.ReadLine();
                        nLine++;
                    }
                }
            }
            catch (Exception)
            {
                //string str1 = string.Format("{0}\x09 0.0\x09 0.0\x09 0.0\x09 0.0", calcpara.code);
                //calcpara.outstream.WriteLine(str1);
                return null;
            }

            if (voltotal == 0)
            {
                return null;
            }

            cRet.date = date;
            cRet.strName = strStockname;
            cRet.vol = voltotal;
            cRet.dataFile = calcpara.filename;
            cRet.fClosePrice = fClosePrice > 0.0F? fClosePrice:arr.Last().price;
            if (cntB > 0)
            {
                cRet.avgvolb = voltotalB / cntB;
            }

            if (cntNB > 0)
            {
                cRet.avgvolnb = voltotalB / cntNB;
            }

            if (cntS > 0)
            {
                cRet.avgvols = voltotalS / cntS;
            }

            if (cntNS > 0)
            {
                cRet.avgvolns = voltotalS / cntNS;
            }

            float avgprice = pricetotal / arr.Count();

            arr.Sort(delegate(Trade t1, Trade t2) { return t2.vol.CompareTo(t1.vol); });
            // System.Windows.Forms.MessageBox.Show(arr[0].vol.ToString());

            int p36 = (int)(voltotal * 0.36);              
            int voltmp = 0;
            float i36, j36;
            MValue m36 = new MValue();

            int i = 0;
            int icnt = 0;
            int jcnt = 0;
            int mcnt = 0;

             if( CalcIJM(arr, ref voltmp, p36, ref i, out i36, out j36, ref m36))
             {                 
                icnt++;
                jcnt++;
             }
             else
             {
                 voltmp = 0;
                 i = 0;
             }

             if (m36.m > 0.0001F)
             {
                 mcnt++;
             }

            int p60 = (int)(voltotal * 0.6);
            float i60, j60;
            MValue m60 = new MValue();

            if( CalcIJM(arr, ref voltmp, p60, ref i, out i60, out j60, ref m60) )  
            {                
                icnt++;
                jcnt++;
                if (m60.m > 0.0001F) 
                {
                    mcnt++;
                    if (mcnt > jcnt) //discard m36
                    {
                        m36.m = 0.0F;
                        m36.vol = 0;
                        mcnt--;
                    }
                }
             }
             else
             {
                 voltmp = 0;
                 i = 0;
             }

            int p80 = (int)(voltotal * 0.8);
            float i80, j80;
            MValue m80 = new MValue();
            if ( CalcIJM(arr, ref voltmp, p80, ref i, out i80, out j80, ref m80) )
             {                 
                icnt++;
                jcnt++;
                if (m80.m > 0.0001F)
                {
                    mcnt++;
                }
             }
             else
             {
                 voltmp = 0;
                 i = 0;
             }

            float ip, jp;
            MValue mp = new MValue();
            if( CalcIJM2(arr, 69.9F, out ip, out jp, ref mp) )
            {
                icnt++;
                jcnt++;
                if (mp.m > 0.0001F)
                {
                    mcnt++;
                }
            }

            cRet.iav = ip;
            if (icnt > 2)
                cRet.iav = (i36 + i60 + i80 + ip) / icnt;

            cRet.jav = jp;
            if (jcnt > 2)
                cRet.jav = (j36 + j60 + j80 + jp) / jcnt;

          
            int volt = m36.vol + m60.vol + m80.vol + mp.vol;
            if (volt > 0)
            {
                cRet.mav = m36.m * (m36.vol / (float)volt) +
                       m60.m * (m60.vol / (float)volt) +
                       m80.m * (m80.vol / (float)volt) +
                       mp.m * (mp.vol / (float)volt);
            }

            if (cRet.mav > 0.001F)
                cRet.weighted = cRet.iav * 0.18F + cRet.jav * 0.57F + cRet.mav * 0.25F;
            else
                cRet.weighted = cRet.iav * 0.3F + cRet.jav * 0.7F;
           
            if (cRet.iav < 0.00001)
            {
                cRet.weighted = 0.0F;
            }
            else if ((cRet.iav < 0.5F) && (cRet.weighted > 1.0F))
            {
                cRet.weighted *= (cRet.iav + 0.35F);
            }

            StringBuilder strbuf = new StringBuilder();
            strbuf.AppendFormat("{0}\x09 {1}\x09 {2}\x09 {3:f2}\x09 {4:f2}\x09 {5:f2}\x09 {6:f2}\x09 {7:f2}\x09 ",
                                  cRet.code, cRet.date, cRet.strName, cRet.fClosePrice, cRet.iav, cRet.jav, cRet.mav, cRet.weighted);
            strbuf.AppendFormat("{0}\x09 {1}\x09 {2}\x09 {3}\x09 {4}\x09 ",
                                 cRet.vol, cRet.avgvolb, cRet.avgvols, cRet.avgvolnb, cRet.avgvolns);
            strbuf.AppendFormat("{0}\x09 {1}\x09 {2}", cRet.bidAM, cRet.bidPM, cRet.dataFile);
     
            System.Threading.Monitor.Enter(calcpara.outstream);
            calcpara.outstream.WriteLine(strbuf.ToString());
            //calcpara.outstream.Flush();
            System.Threading.Monitor.Exit(calcpara.outstream);
            return cRet;
        }

        private static bool CalcIJM(List<Trade> arr, ref int voltmp, int p, ref int i, out float io, out float jo, ref MValue mo)
        {
            int volb = 0;
            int vols = 0;
            int opb = 0;
            int ops = 0;
            int mb = 0;
            int ms = 0;
            mo.vol = 0;

            int count = i;
            int cntb = 0;
            int cnts = 0;
            Trade t1;            
            bool bMetVoltmp = false;


            for (; i < arr.Count(); i++)
            {
                t1 = arr[i];
                voltmp += t1.vol;
                if (t1.flag == 'B')
                {
                    volb += t1.vol;
                    opb += t1.op;
                    if (t1.op < 3)
                    {
                        mb++;
                        mo.vol += t1.vol;
                    }
                    cntb++;
                }
                else if (t1.flag == 'S')
                {
                    vols += t1.vol;
                    ops += t1.op;
                    if (t1.op < 3)
                    {
                        ms++;
                        mo.vol += t1.vol;
                    }
                    cnts++;
                }

                if (!bMetVoltmp && voltmp >= p)
                {
                    bMetVoltmp = true;
                }
                
                if (bMetVoltmp && t1.vol > arr[i].vol)
                {
                    break;
                }
            }

            count = i - count;

            io = 0.0F;
            jo = 0.0F;
            mo.m = calcMValue(ms, mb);

            if ( (count < 10) || (cntb < 3) || (cnts < 3) )
            {
                return false;
            }
            else
            {           
                io = (float)volb / vols;
                opb = opb > 0 ? opb : 1;
                ops = ops > 0 ? ops : 1;
                volb = volb > 0 ? volb : 1;
                jo = ((float)vols / ops) / ((float)volb / opb);

                return true;
            }
        }

        private static bool CalcIJM2(List<Trade> arr, float p, out float io, out float jo, ref MValue mo)
        {
            int volb = 0;
            int vols = 0;
            int opb = 0;
            int ops = 0;
            int mb = 0;
            int ms = 0;
            int count = 0;
            int cntb = 0;
            int cnts = 0;
            int cntNotMet = 0;
            mo.vol = 0;

            foreach (Trade t1 in arr)
            {
                count++;
                float amount = t1.vol * t1.price;
                if (amount > p)
                {
                    if (t1.flag == 'B')
                    {
                        volb += t1.vol;
                        opb += t1.op;
                        if (t1.op < 3)
                        {
                            mb++;
                            mo.vol += t1.vol;
                        }
                        cntb++;
                    }
                    else if (t1.flag == 'S')
                    {
                        vols += t1.vol;
                        ops += t1.op;
                        if (t1.op < 3)
                        {
                            ms++;
                            mo.vol += t1.vol;
                        }

                        cnts++;
                    }

                    cntNotMet = 0;
                }
                else
                {
                    cntNotMet++;
                    if (cntNotMet > 18)
                        break;
                }
            }

            io = 0.0F;
            jo = 0.0F;
            mo.m = calcMValue(ms, mb);
           
            if ((count < 10) || (cntb < 3) || (cnts < 3))
            {
                return false;
            }
            else
            {
                io = (float)volb / vols;
                opb = opb > 0 ? opb : 1;
                ops = ops > 0 ? ops : 1;
                volb = volb > 0 ? volb : 1;
                jo = ((float)vols / ops) / ((float)volb / opb);    
            
                return true;
            }
        }

        private static float calcMValue(int ms, int mb)
        {
            float mo = 0.0F;

            if ((mb == 0) && ms > 0)
            {
                mo = (float)ms;
            }
            else if(mb > 0)
            {
                if (ms == 0)
                {
                    ms = 1;
                    mb *= 2;
                }
                mo = (float)ms / mb;
            }
            return mo;
        }
    }
}
